﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace ZeroBug.EasyFlow.Core
{
    public class FlowManager
    {
        private bool _breakFlag;
        public SerializableDictionary<string, SerializableDictionary<string, SignalBase>> FlowSignals;
        private LogHelper _logHelper;
        private Dictionary<string, Type> _stepOperators;
        [XmlIgnore]
        public VariableManager VariableManager {  get; set; }
        public SerializableDictionary<string, FlowBase>? Flows { get; set; }
        /// <summary>
        /// bool参数为true时代表启动，bool参数为false时代表暂停
        /// </summary>
        public event Action<bool>? OnFlowStartPause;
        public string SolutionName { get; set; }
        public Recipe CurrentRecipe { get; set; }
        private bool _pauseFlag = true;
        [XmlIgnore]
        public bool PauseFlag
        {
            get => _pauseFlag;
            set
            {
                _pauseFlag = value;
                foreach (var item in Flows)
                {
                    item.Value.PauseFlag = _pauseFlag;
                }
            }
        }
        [XmlIgnore]
        public bool AllFlowPaused
        {
            get => Flows!.Values.ToList().All(flow => (flow.IsEnabled && flow.Status == FlowStatus.Paused) || !flow.IsEnabled);
        }
        [XmlIgnore]
        public bool AllFlowStopped
        {
            get => Flows!.Values.ToList().All(flow => !flow.IsBackground && flow.Status == FlowStatus.Stopped);
        }
        [XmlIgnore]
        public bool AllFlowHomeFinished
        {
            get => !(Flows!.Values.ToList().Any(flow => (flow.IsEnabled && !flow.IsHomeFinish) || !flow.IsEnabled));
        }
        public FlowManager()
        {
            this.FlowSignals = new SerializableDictionary<string, SerializableDictionary<string, SignalBase>>();
            this.Flows = new SerializableDictionary<string, FlowBase>();
            this.VariableManager = new VariableManager();
            this.CurrentRecipe = new Recipe(this);
        }
        public FlowManager(VariableManager varManager, LogHelper logger, AssemblyHelper assemblyHelper)
            : this()
        {
            this.VariableManager = varManager;
            this._logHelper = logger;
            this._stepOperators = assemblyHelper.AssemblyTypes;
        }

        #region 流程操作
        /// <summary>
        /// 打开所有流程，开始线程运行
        /// </summary>
        public void Open()
        {
            foreach (var flow in Flows!.Values)
            {
                flow.Open();
            }
        }
        public bool Compile(out string errInfo)
        {
            errInfo = string.Empty;
            foreach (var flow in Flows!.Values)
            {
                flow.Compile(out errInfo);
                if (!string.IsNullOrEmpty(errInfo))
                    return false;
            }
            return true;
        }
        public bool Prepare(out string errInfo)
        {
            errInfo = string.Empty;
            foreach (var flow in Flows!.Values)
            {
                flow.Prepare(out errInfo);
                if (!string.IsNullOrEmpty(errInfo))
                    return false;
            }
            return true;
        }
        /// <summary>
        /// 第一次启动或每次停止后 执行，需初始化所有的线程，复位各个信号
        /// </summary>
        public void Init(out string errInfo)
        {
            errInfo = "";
            _breakFlag = false;
            //复位所有线程间通信信号
            ResetAllSignals();
            foreach (FlowBase flow in Flows!.Values)
            {
                flow.Init(out errInfo);
                //若有一个流程初始化失败，即结束
                if (!string.IsNullOrEmpty(errInfo)) return;
            }
        }
        public void Start()
        {
            this.PauseFlag = false;
            OnFlowStartPause?.Invoke(true);
        }
        public void Pause()
        {
            this.PauseFlag = true;
            OnFlowStartPause?.Invoke(false);
        }
        public void Step()
        {
            foreach (FlowBase flow in Flows!.Values)
            {
                flow.IsStepRun = true;
            }
        }
        /// <summary>
        /// 停止所有正在执行中的线程，从头开始运行
        /// </summary>
        public void Stop()
        {
            //暂停信号给出
            Pause();
            //跳出所有正在等待的信号
            BreakAllSignals();
            foreach (FlowBase flow in Flows!.Values)
            {
                flow.OnStop();
            }
            //跳出所有可能的循环
            _breakFlag = true;
        }
        /// <summary>
        /// 关闭所有流程，停止线程运行
        /// </summary>
        public void Close()
        {
            foreach (var flow in Flows!.Values)
            {
                flow.Close();
            }
        }
        public bool IsBreak()
        {
            return _breakFlag;
        }
        public void ResetBreak()
        {
            _breakFlag = false;
        }
        public bool WaitForStart(FlowBase flow)
        {
            //暂停信号有且 break信号无时，等待
            while (flow.PauseFlag && !_breakFlag)
            {
                System.Threading.Thread.Sleep(2);
            }
            if (_breakFlag) return false;
            return true;
        }
        #endregion

        #region 流程间信号操作
        public void RegisterSignal(string flowName, string signalName, bool initState = false)
        {
            SignalBase signal = new SignalBase(initState, true, Flows[flowName]);
            if (!this.FlowSignals.ContainsKey(flowName))
                this.FlowSignals.Add(flowName, new SerializableDictionary<string, SignalBase>());
            this.FlowSignals[flowName].Add(signalName, signal);
        }
        public bool WaitSignal(string fromFlowName, string signalName)
        {
            return this.FlowSignals[fromFlowName][signalName].Wait();
        }
        public bool CheckSignal(string fromFlowName, string signalName)
        {
            return this.FlowSignals[fromFlowName][signalName].GetStatus();
        }
        public SignalBase GetSignal(string flowName, string signalName)
        {
            return this.FlowSignals[flowName][signalName];
        }
        public void SetSignal(string flowName, string signalName)
        {
            this.FlowSignals[flowName][signalName].Set();
        }
        public void ResetSignal(string flowName, string signalName)
        {
            this.FlowSignals[flowName][signalName].Reset();
        }
        public void ResetAllSignals()
        {
            FlowSignals.Values.ToList().ForEach(signalDict => signalDict.ToList().ForEach(signal =>
            {
                signal.Value.ResetBreak();
                signal.Value.Reset();
            }));
        }
        public void BreakAllSignals()
        {
            FlowSignals.Values.ToList().ForEach(signalDict => signalDict.ToList().ForEach(signal =>
            {
                signal.Value.Break();
            }));
        }
        #endregion

        public void Save(string solutionName)
        {
            VariableManager.Save(solutionName);
            XmlSerializerHelper<FlowManager>.SerializeToFile(this, PathHelper.GetConfigPath("Solutions" + $"\\{SolutionName}") + "Solution.xml");
        }
        public void Load(string solutionName)
        {
            VariableManager.Load(solutionName);
            FlowManager flowManager = XmlSerializerHelper<FlowManager>.DeserializeFromFile(PathHelper.GetConfigPath("Solutions" + $"\\{solutionName}") + "Solution.xml");
            this.Flows = flowManager.Flows;
            this.SolutionName = flowManager.SolutionName;
            this.FlowSignals = flowManager.FlowSignals;
            foreach (var item in this.FlowSignals)
            {
                foreach (var signal in item.Value)
                {
                    signal.Value.SetFlow(this.Flows[item.Key]);
                }
            }
            this.Flows.Values.ToList().ForEach(flow =>
            {
                flow.SetFlowManager(this);
                flow.Open();
                flow.LoadLocalVariables();
            });
            this.SetStepOperators(_stepOperators.Where(kvp => kvp.Value.IsAssignableTo(typeof(IStep))).ToDictionary()!);
        }
        public void LoadRecipe(string recipeName)
        {
            CurrentRecipe = XmlSerializerHelper<Recipe>.DeserializeFromFile(PathHelper.GetConfigPath("Solutions" + $"\\{SolutionName}\\Recipes") + $"{recipeName}.xml");
            CurrentRecipe.LoadOriginalValue();
            CurrentRecipe.SetFlowManager(this);
            CurrentRecipe.Variables.ToList().ForEach(variable => { this.VariableManager.AddVariable(variable.Value, VariableFrom.Recipe); });
        }
        public void SaveRecipe(string recipeName)
        {
            XmlSerializerHelper<Recipe>.SerializeToFile(CurrentRecipe, PathHelper.GetConfigPath("Solutions" + $"\\{SolutionName}\\Recipes") + $"{recipeName}.xml");
        }
        public Dictionary<string, Type> GetStepOperators()
        {
            return _stepOperators;
        }
        public void SetStepOperators(Dictionary<string, Type> stepOperators)
        {
            _stepOperators = stepOperators;
        }
        public LogHelper GetLogger()
        {
            return _logHelper;
        }
        public void ResetVariables()
        {
            foreach (var item in Flows)
            {
                item.Value.ResetVariables();
            }
        }
    }
}
